// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/child/service_worker/web_service_worker_registration_impl.h"

#include <utility>

#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "content/child/service_worker/service_worker_dispatcher.h"
#include "content/child/service_worker/service_worker_registration_handle_reference.h"
#include "content/child/service_worker/web_service_worker_impl.h"
#include "content/child/service_worker/web_service_worker_provider_impl.h"
#include "content/common/service_worker/service_worker_types.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebNavigationPreloadState.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerError.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRegistrationProxy.h"

namespace content {

namespace {

    class HandleImpl : public blink::WebServiceWorkerRegistration::Handle {
    public:
        explicit HandleImpl(
            const scoped_refptr<WebServiceWorkerRegistrationImpl>& registration)
            : registration_(registration)
        {
        }
        ~HandleImpl() override { }

        blink::WebServiceWorkerRegistration* registration() override
        {
            return registration_.get();
        }

    private:
        scoped_refptr<WebServiceWorkerRegistrationImpl> registration_;

        DISALLOW_COPY_AND_ASSIGN(HandleImpl);
    };

} // namespace

WebServiceWorkerRegistrationImpl::QueuedTask::QueuedTask(
    QueuedTaskType type,
    const scoped_refptr<WebServiceWorkerImpl>& worker)
    : type(type)
    , worker(worker)
{
}

WebServiceWorkerRegistrationImpl::QueuedTask::QueuedTask(
    const QueuedTask& other)
    = default;

WebServiceWorkerRegistrationImpl::QueuedTask::~QueuedTask() { }

WebServiceWorkerRegistrationImpl::WebServiceWorkerRegistrationImpl(
    std::unique_ptr<ServiceWorkerRegistrationHandleReference> handle_ref)
    : handle_ref_(std::move(handle_ref))
    , proxy_(nullptr)
{
    DCHECK(handle_ref_);
    DCHECK_NE(kInvalidServiceWorkerRegistrationHandleId,
        handle_ref_->handle_id());
    ServiceWorkerDispatcher* dispatcher = ServiceWorkerDispatcher::GetThreadSpecificInstance();
    DCHECK(dispatcher);
    dispatcher->AddServiceWorkerRegistration(handle_ref_->handle_id(), this);
}

void WebServiceWorkerRegistrationImpl::SetInstalling(
    const scoped_refptr<WebServiceWorkerImpl>& service_worker)
{
    if (proxy_)
        proxy_->setInstalling(WebServiceWorkerImpl::CreateHandle(service_worker));
    else
        queued_tasks_.push_back(QueuedTask(INSTALLING, service_worker));
}

void WebServiceWorkerRegistrationImpl::SetWaiting(
    const scoped_refptr<WebServiceWorkerImpl>& service_worker)
{
    if (proxy_)
        proxy_->setWaiting(WebServiceWorkerImpl::CreateHandle(service_worker));
    else
        queued_tasks_.push_back(QueuedTask(WAITING, service_worker));
}

void WebServiceWorkerRegistrationImpl::SetActive(
    const scoped_refptr<WebServiceWorkerImpl>& service_worker)
{
    if (proxy_)
        proxy_->setActive(WebServiceWorkerImpl::CreateHandle(service_worker));
    else
        queued_tasks_.push_back(QueuedTask(ACTIVE, service_worker));
}

void WebServiceWorkerRegistrationImpl::OnUpdateFound()
{
    if (proxy_)
        proxy_->dispatchUpdateFoundEvent();
    else
        queued_tasks_.push_back(QueuedTask(UPDATE_FOUND, nullptr));
}

void WebServiceWorkerRegistrationImpl::setProxy(
    blink::WebServiceWorkerRegistrationProxy* proxy)
{
    proxy_ = proxy;
    RunQueuedTasks();
}

void WebServiceWorkerRegistrationImpl::RunQueuedTasks()
{
    DCHECK(proxy_);
    for (const QueuedTask& task : queued_tasks_) {
        if (task.type == INSTALLING)
            proxy_->setInstalling(WebServiceWorkerImpl::CreateHandle(task.worker));
        else if (task.type == WAITING)
            proxy_->setWaiting(WebServiceWorkerImpl::CreateHandle(task.worker));
        else if (task.type == ACTIVE)
            proxy_->setActive(WebServiceWorkerImpl::CreateHandle(task.worker));
        else if (task.type == UPDATE_FOUND)
            proxy_->dispatchUpdateFoundEvent();
    }
    queued_tasks_.clear();
}

blink::WebServiceWorkerRegistrationProxy*
WebServiceWorkerRegistrationImpl::proxy()
{
    return proxy_;
}

blink::WebURL WebServiceWorkerRegistrationImpl::scope() const
{
    return handle_ref_->scope();
}

void WebServiceWorkerRegistrationImpl::update(
    blink::WebServiceWorkerProvider* provider,
    std::unique_ptr<WebServiceWorkerUpdateCallbacks> callbacks)
{
    WebServiceWorkerProviderImpl* provider_impl = static_cast<WebServiceWorkerProviderImpl*>(provider);
    ServiceWorkerDispatcher* dispatcher = ServiceWorkerDispatcher::GetThreadSpecificInstance();
    DCHECK(dispatcher);
    dispatcher->UpdateServiceWorker(provider_impl->provider_id(),
        registrationId(), std::move(callbacks));
}

void WebServiceWorkerRegistrationImpl::unregister(
    blink::WebServiceWorkerProvider* provider,
    std::unique_ptr<WebServiceWorkerUnregistrationCallbacks> callbacks)
{
    WebServiceWorkerProviderImpl* provider_impl = static_cast<WebServiceWorkerProviderImpl*>(provider);
    ServiceWorkerDispatcher* dispatcher = ServiceWorkerDispatcher::GetThreadSpecificInstance();
    DCHECK(dispatcher);
    dispatcher->UnregisterServiceWorker(provider_impl->provider_id(),
        registrationId(), std::move(callbacks));
}

void WebServiceWorkerRegistrationImpl::enableNavigationPreload(
    bool enable,
    blink::WebServiceWorkerProvider* provider,
    std::unique_ptr<WebEnableNavigationPreloadCallbacks> callbacks)
{
    WebServiceWorkerProviderImpl* provider_impl = static_cast<WebServiceWorkerProviderImpl*>(provider);
    ServiceWorkerDispatcher* dispatcher = ServiceWorkerDispatcher::GetThreadSpecificInstance();
    DCHECK(dispatcher);
    dispatcher->EnableNavigationPreload(provider_impl->provider_id(),
        registrationId(), enable,
        std::move(callbacks));
}

void WebServiceWorkerRegistrationImpl::getNavigationPreloadState(
    blink::WebServiceWorkerProvider* provider,
    std::unique_ptr<WebGetNavigationPreloadStateCallbacks> callbacks)
{
    WebServiceWorkerProviderImpl* provider_impl = static_cast<WebServiceWorkerProviderImpl*>(provider);
    ServiceWorkerDispatcher* dispatcher = ServiceWorkerDispatcher::GetThreadSpecificInstance();
    DCHECK(dispatcher);
    dispatcher->GetNavigationPreloadState(provider_impl->provider_id(),
        registrationId(), std::move(callbacks));
}

void WebServiceWorkerRegistrationImpl::setNavigationPreloadHeader(
    const blink::WebString& value,
    blink::WebServiceWorkerProvider* provider,
    std::unique_ptr<WebSetNavigationPreloadHeaderCallbacks> callbacks)
{
    WebServiceWorkerProviderImpl* provider_impl = static_cast<WebServiceWorkerProviderImpl*>(provider);
    ServiceWorkerDispatcher* dispatcher = ServiceWorkerDispatcher::GetThreadSpecificInstance();
    DCHECK(dispatcher);
    dispatcher->SetNavigationPreloadHeader(provider_impl->provider_id(),
        registrationId(), value.utf8(),
        std::move(callbacks));
}

int64_t WebServiceWorkerRegistrationImpl::registrationId() const
{
    return handle_ref_->registration_id();
}

// static
std::unique_ptr<blink::WebServiceWorkerRegistration::Handle>
WebServiceWorkerRegistrationImpl::CreateHandle(
    const scoped_refptr<WebServiceWorkerRegistrationImpl>& registration)
{
    if (!registration)
        return nullptr;
    return base::MakeUnique<HandleImpl>(registration);
}

WebServiceWorkerRegistrationImpl::~WebServiceWorkerRegistrationImpl()
{
    ServiceWorkerDispatcher* dispatcher = ServiceWorkerDispatcher::GetThreadSpecificInstance();
    if (dispatcher)
        dispatcher->RemoveServiceWorkerRegistration(handle_ref_->handle_id());
}

} // namespace content
